﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using SMS.Remote;
using System.Threading;
using System.Windows.Forms;
using SMS.Core.Properties;
using SMS.Core.DataEntity;
using SMS.Core.DataAccess;

namespace SMS.Core
{
    /// <summary>
    /// 串口处理类
    /// </summary>
    public static class CommHandler
    {
        public static event SendEventHandler OnSend;
        public static event ReceiveEventHandler OnReceive;
        public static event MessageEventHandler OnMessage;

        static CommHandler()
        {
            Initialization();
            //远程通信事件
            //ServerContactor.Initialize();
            //ServerRemote.OnReceive += new MessageEventHandler(ServerRemote_OnReceive);
        }

        /// <summary>
        /// 初始化
        /// </summary>
        private static void Initialization()
        {
            MMScomm = new SerialPort();
            MMScomm.PortName = CommConfig.PortName;
            MMScomm.BaudRate = CommConfig.BaudRate;
            MMScomm.Parity = CommConfig.Parity;
            MMScomm.DataBits = CommConfig.DataBits;
            MMScomm.StopBits = CommConfig.StopBits;
            MMScomm.RtsEnable = CommConfig.RtsEnable;
            MMScomm.DtrEnable = CommConfig.DtrEnable;

            MMScomm.DataReceived += new SerialDataReceivedEventHandler(MMScomm_DataReceived);

            SendState = true;
            AutoSendState = true;
            mComm = new CommOperation();
            mSMS = new SMSInterpreter();
            mPDU = new STPDU();
            mCommParam = new STCommParam(true);
        }

        #region 成员属性
        /// <summary>
        /// 重试等待时间
        /// </summary>
        private const int RETRY_WAIT_TIME = 300;
        /// <summary>
        /// 串口操作类的实例对象
        /// </summary>
        static CommOperation mComm;
        /// <summary>
        /// 短信、PDU操作类
        /// </summary>
        static SMSInterpreter mSMS;
        /// <summary>
        /// 待发送j码
        /// </summary>
        static STPDU mPDU;
        /// <summary>
        /// 是否在串口控件中处理接收到的信息
        /// </summary>
        static STCommParam mCommParam;
        /// <summary>
        /// 接收到的数据
        /// </summary>
        static string mReceiveData;
        /// <summary>
        /// 串口
        /// </summary>
        public static SerialPort MMScomm;
        /// <summary>
        /// 发送号码
        /// </summary>
        public static string SendNumber;
        /// <summary>
        /// 发送状态
        /// </summary>
        public static bool SendState;
        /// <summary>
        /// 自动发送状态
        /// </summary>
        public static bool AutoSendState;
        /// <summary>
        /// 自动接收状态
        /// </summary>
        public static bool AutoReceiveState;
        /// <summary>
        /// 立即处理状态
        /// </summary>
        public static bool ImmediateState;
        private static bool sm_ConnectState;
        /// <summary>
        /// 连接状态
        /// </summary>
        public static bool ConnectionState
        {
            get { return sm_ConnectState && MMScomm.IsOpen; }
            set { sm_ConnectState = true; }
        }
        /// <summary>
        /// 获取串口打开状态
        /// </summary>
        private static bool CommOpenState
        {
            get { return MMScomm.IsOpen; }
        }

        #endregion

        #region 触发事件

        static void MMScomm_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string msg = string.Empty;
            int retries = 0;
            bool recvOk = false;
            int recvLen = 0;
            byte[] recvBytes;
            string recvText = string.Empty;

            if (!mCommParam.CommRecvEvent) return;
            {
                Thread.Sleep(mCommParam.RecvWaitTime);
                recvLen = MMScomm.BytesToRead;
            }
            mCommParam.RecvWaitTime = 100;

            if (recvLen > 0)
            {
                recvBytes = new byte[MMScomm.BytesToRead];
                MMScomm.Read(recvBytes, 0, MMScomm.BytesToRead);
                recvText = Encoding.ASCII.GetString(recvBytes);

                FireOnReceive(null, recvText);

                //接收短信
                if (recvText.IndexOf("+CMTI:") > 0)
                {
                    var recvTemp = recvText;
                    int cmtiIndex = recvTemp.IndexOf("+CMTI:");
                    recvTemp = recvTemp.Substring(cmtiIndex);
                    int rIndex = recvTemp.IndexOf("\r");
                    recvTemp = recvTemp.Substring(0, rIndex);
                    cmtiIndex = recvTemp.IndexOf(",");
                    var recvFlag = recvTemp.Substring(cmtiIndex + 1);
                    recvOk = ReceiveSMS(recvFlag, out retries, out msg);
                    //解释接收到的短信
                    if (recvOk)
                    {                         
                        STSMS sTSMS = mSMS.ParsePDU_CMGR(msg, true);
                        //TODO:将收到的短信进行处理
                        //删除短信
                        DeleteSMS(recvFlag, out retries, out msg);
                    }
                }
                //处理接收到的信息
                else
                {
                    mReceiveData = recvText;
                }
            }
        }

        static void ServerRemote_OnReceive(string message)
        {
            //TODO:收到客户端发过来的信息
        }

        private static void FireOnMessage(string msg)
        {
            if (OnMessage != null) OnMessage(msg);
            try
            {
                var remoteMgr = new RemoteManager();
                remoteMgr.SendServerMessage(msg);
            }
            catch { }
        }

        private static void FireOnSend(string number, string message)
        {
            if (OnSend != null) OnSend(number, message);
        }

        private static void FireOnReceive(string number, string message)
        {
            if (OnReceive != null) OnReceive(number, message);
        }

        #endregion

        #region 启动\停止Modem

        /// <summary>
        /// 启动串口
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static bool Start(out string msg)
        {
            msg = string.Empty;
            var retries = 0;
            var startOk = false;
            var message = string.Empty;

            startOk = mComm.MMSOpen(MMScomm);
            if (startOk)
            {
                msg = Resources.CommOpen_Success;
            }
            else
            {
                msg = Resources.CommOpen_Fail;
            }
            FireOnMessage(msg);

            if (startOk)
            {
                startOk = ConnectModem(out retries, out message);
                if (string.IsNullOrEmpty(message)) message = Resources.ConnectModem_Fail;
                msg += string.Format("{0}{1}", Environment.NewLine, message);
                if(startOk) FireOnMessage(Resources.ConnectModem_Success);
                else FireOnMessage(Resources.ConnectModem_Fail);
            }

            if (startOk)
            {
                startOk = GetCenterNumber(out retries, out message);
                if (string.IsNullOrEmpty(message)) message = Resources.GetCenterNumber_Fail;
                msg += string.Format("{0}{1}", Environment.NewLine, message);
                if (startOk) FireOnMessage(Resources.GetCenterNumber_Success);
                else FireOnMessage(Resources.GetCenterNumber_Fail);
            }
            if (startOk)
            {
                ConnectionState = true;
                StartCommThread();
            }
            return startOk;
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static bool Stop(out string msg)
        {
            msg = string.Empty;
            var message = string.Empty;
            var stopOk = false;

            stopOk = mComm.MMSClose(MMScomm);
            if (stopOk)
            {
                msg = Resources.CommClose_Success;
            }
            else
            {
                msg = Resources.CommClose_Fail;
            }
            if (stopOk) StopCommThread();
            FireOnMessage(msg);
            return stopOk;
        }

        #endregion

        #region 短信命令处理

        /// <summary>
        /// 连接短信猫
        /// </summary>
        /// <param name="retries"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static bool ConnectModem(out int retries, out string msg)
        {
            retries = 0;
            msg = string.Empty;
            var exType = ExceptionType.Unknow;
            var sendOk = false;

            var sendCommand = string.Format("ATE\r\n");
            mCommParam.SendKeyWord = "ATE";
            mCommParam.SendCommand = sendCommand;

            while ((sendOk = SendCommand(out msg, out exType)) == false)
            {
                retries++;
                if (retries >= CommConfig.Retries) break;
                Thread.Sleep(RETRY_WAIT_TIME);
            }
            return sendOk;
        }
        
        /// <summary>
        /// 获取短信中心号码
        /// </summary>
        /// <param name="retries"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static bool GetCenterNumber(out int retries, out string msg)
        {
            retries = 0;
            msg = string.Empty;
            var exType = ExceptionType.Unknow;
            var sendOk = false;

            var sendCommand = string.Format("AT+CSCA?\r\n");
            mCommParam.SendKeyWord = "CSCA";
            mCommParam.SendCommand = sendCommand;

            while ((sendOk = SendCommand(out msg, out exType)) == false)
            {
                retries++;
                if (retries >= CommConfig.Retries) break;
                Thread.Sleep(RETRY_WAIT_TIME);
            }
            return sendOk;
        }

        /// <summary>
        /// 发送短信
        /// </summary>
        /// <param name="sendNumber"></param>
        /// <param name="sendMsg"></param>
        /// <param name="retries"></param>
        /// <param name="msg"></param>
        /// <param name="exType"></param>
        /// <returns></returns>
        public static bool SendSMS(string sendNumber, string sendMsg, out int retries, out string msg, out ExceptionType exType)
        {
            retries = 0;
            msg = string.Empty;
            exType = ExceptionType.Unknow;
            var sendOk = false;

            STPDUParam pduParam = new STPDUParam(true);
            pduParam.DAdress = sendNumber.Trim();
            pduParam.UserData = sendMsg.Trim();
            pduParam.ISSCA = EISSCA.YES;
            pduParam.DataCodingScheme.CodeScheme = ECodeScheme.Unicode;
            //if (useUnicode)
            //{
            //    pduParam.DataCodingScheme.CodeScheme = ECodeScheme.Unicode;
            //}
            //else
            //{
            //    pduParam.DataCodingScheme.CodeScheme = ECodeScheme.Sbit;
            //}
            mPDU = mSMS.GeneratePDU(pduParam);
            if (mPDU.strPDU.IndexOf("ERROR") > 0)
            {
                msg = Resources.CreatePDU_Error;
                sendOk = false;
            }
            else
            {
                var pduRetries = 0;
                while ((sendOk = SendSMS(mPDU, out pduRetries, out msg, out exType)) == false)
                {
                    retries++;
                    if (retries >= CommConfig.Retries) break;
                    Thread.Sleep(RETRY_WAIT_TIME);
                }
            }
            return sendOk;
        }
        
        /// <summary>
        /// 发送短信
        /// </summary>
        /// <param name="sTPDU"></param>
        /// <param name="retries"></param>
        /// <param name="msg"></param>
        /// <param name="exType"></param>
        /// <returns></returns>
        public static bool SendSMS(STPDU sTPDU, out int retries, out string msg, out ExceptionType exType)
        {
            retries = 0;
            msg = string.Empty;
            exType = ExceptionType.Unknow;
            var sendOk = false;

            var sendCommand = string.Format("AT+CMGS={0}\r\n", sTPDU.lenPDU);
            mCommParam.SendKeyWord = "CMGS";
            mCommParam.SendCommand = sendCommand;
            while ((sendOk = SendCommand(out msg, out exType)) == false)
            {
                retries++;
                if (retries >= CommConfig.Retries) break;
                Thread.Sleep(RETRY_WAIT_TIME);
            }
            if (sendOk == false)
            {
                msg = Resources.SendPDULength_Fail;
                return sendOk;
            }

            mCommParam.SendKeyWord = "CMGS>";
            mCommParam.SendCommand = sTPDU.strPDU;

            byte[] sendBytes = new byte[sTPDU.strPDU.Length + 1];
            Encoding.ASCII.GetBytes(sTPDU.strPDU, 0, sTPDU.strPDU.Length, sendBytes, 0);
            sendBytes[sTPDU.strPDU.Length] = 0x1A;
            sendOk = SendCommand(sendBytes, out msg, out exType);

            return sendOk;
        }

        /// <summary>
        /// 接收短信
        /// </summary>
        /// <param name="recvFlag"></param>
        /// <param name="retries"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static bool ReceiveSMS(string recvFlag, out int retries, out string msg)
        {
            retries = 0;
            msg = string.Empty;
            var exType = ExceptionType.Unknow;
            var sendOk = false;

            var sendCommand = string.Format("AT+CMGR={0}\r\n", recvFlag);
            mCommParam.SendKeyWord = "CMGR";
            mCommParam.SendCommand = sendCommand;
            mCommParam.RecvFlag = recvFlag;
            mCommParam.RecvWaitTime = 200;

            while ((sendOk = SendCommand(out msg, out exType)) == false)
            {
                retries++;
                if (retries >= CommConfig.Retries) break;
                Thread.Sleep(RETRY_WAIT_TIME);
            }
            return sendOk;
        }

        /// <summary>
        /// 删除短信
        /// </summary>
        /// <param name="recvFlag"></param>
        /// <param name="retries"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static bool DeleteSMS(string recvFlag, out int retries, out string msg)
        {
            retries = 0;
            msg = string.Empty;
            var exType = ExceptionType.Unknow;
            var sendOk = false;

            var sendCommand = string.Format("AT+CMGD={0}\r\n", recvFlag);
            mCommParam.SendKeyWord = "CMGD";
            mCommParam.SendCommand = sendCommand;
            mCommParam.RecvFlag = recvFlag;

            while ((sendOk = SendCommand(out msg, out exType)) == false)
            {
                retries++;
                if (retries > CommConfig.Retries) break;
                Thread.Sleep(RETRY_WAIT_TIME);
            }
            return sendOk;
        }

        /// <summary>
        /// 发送命令到串口
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static bool SendCommand(out string msg, out ExceptionType exType)
        {
            var sendOk = false;
            msg = string.Empty;
            exType = ExceptionType.Unknow;
            if (CommOpenState == false)
            {
                exType = ExceptionType.Inner;
                msg = Resources.Comm_NonOpen;
                return sendOk;
            }
            if (SendState == false)
            {
                exType = ExceptionType.Inner;
                msg = Resources.SendState_Error;
                return sendOk;
            }
            if (string.IsNullOrEmpty(mCommParam.SendCommand))
            {
                exType = ExceptionType.Outer;
                msg = Resources.SendOrder_Empty;
                return sendOk;
            }
            if (string.IsNullOrEmpty(mCommParam.SendKeyWord))
            {
                exType = ExceptionType.Outer;
                msg = Resources.SendKey_Empty;
                return sendOk;
            }
            SendState = false;
            try
            {
                MMScomm.DiscardInBuffer();
                MMScomm.Write(mCommParam.SendCommand);
                FireOnSend(null, mCommParam.SendCommand);

                var startTime = DateTime.Now;
                var repeatNum = 0;
                while (true)
                {
                    Application.DoEvents();
                    Thread.Sleep(10);

                    if (!string.IsNullOrEmpty(mReceiveData))
                    {
                        msg = mReceiveData;
                        if (mReceiveData.IndexOf("OK") > 0 || mReceiveData.IndexOf(">") > 0)
                        {
                            sendOk = true;
                        }
                        break;
                    }
                    repeatNum++;
                    var timeSpan = DateTime.Now - startTime;
                    if (repeatNum >= 1000) break;
                }
                mReceiveData = string.Empty;
                return sendOk;
            }
            finally
            {
                SendState = true;
            }
        }

        /// <summary>
        /// 发送命令到串口
        /// </summary>
        /// <param name="command"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static bool SendCommand(string command, out string msg, out ExceptionType exType)
        {
            var sendOk = false;
            msg = string.Empty;
            exType = ExceptionType.Unknow;
            if (CommOpenState == false)
            {
                exType = ExceptionType.Inner;
                msg = Resources.Comm_NonOpen;
                return sendOk;
            }
            if (SendState == false)
            {
                exType = ExceptionType.Inner;
                msg = Resources.SendState_Error;
                return sendOk;
            }
            SendState = false;
            try
            {
                command += "\r\n";
                MMScomm.DiscardInBuffer();
                MMScomm.Write(command);
                FireOnSend(null, command);
                var startTime = DateTime.Now;
                var repeatNum = 0;
                while (true)
                {
                    Application.DoEvents();
                    Thread.Sleep(10);

                    if (!string.IsNullOrEmpty(mReceiveData))
                    {
                        msg = mReceiveData;
                        if (mReceiveData.IndexOf("OK") > 0 || mReceiveData.IndexOf(">") > 0)
                        {
                            sendOk = true;
                        }
                        break;
                    }
                    repeatNum++;
                    var timeSpan = DateTime.Now - startTime;
                    if (repeatNum >= 1000) break;
                }
                mReceiveData = string.Empty;
                return sendOk;
            }
            finally
            {
                SendState = true;
            }
        }

        /// <summary>
        /// 发送命令到串口
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static bool SendCommand(byte[] bytes, out string msg, out ExceptionType exType)
        {
            var sendOk = false;
            msg = string.Empty;
            exType = ExceptionType.Unknow;
            if (CommOpenState == false)
            {
                exType = ExceptionType.Inner;
                msg = Resources.Comm_NonOpen;
                return sendOk;
            }
            if (SendState == false)
            {
                exType = ExceptionType.Inner;
                msg = Resources.SendState_Error;
                return sendOk;
            }
            SendState = false;
            try
            {
                MMScomm.DiscardInBuffer();
                MMScomm.Write(bytes, 0, bytes.Length);
                FireOnSend(null, Encoding.Default.GetString(bytes));
                var startTime = DateTime.Now;
                var repeatNum = 0;
                while (true)
                {
                    Application.DoEvents();
                    Thread.Sleep(10);

                    if (!string.IsNullOrEmpty(mReceiveData))
                    {
                        msg = mReceiveData;
                        if (mReceiveData.IndexOf("OK") > 0 || mReceiveData.IndexOf(">") > 0)
                        {
                            sendOk = true;
                        }
                        break;
                    }
                    repeatNum++;
                    var timeSpan = DateTime.Now - startTime;
                    if (repeatNum >= 1000) break;
                }
                mReceiveData = string.Empty;
                return sendOk;
            }
            finally
            {
                SendState = true;
            }
        }

        #endregion

        #region 短信业务处理

        private static object CommLock;
        private static Mutex CommMutex;

        private static Thread SendThread;
        private static Thread ReceiveThread;

        private static List<SendMessage> SendList;
        private static List<object> ReceiveList;

        private const int SendWaitTime = 3000;
        private const int ReceiveWaitTime = 1000;

        /// <summary>
        /// 启动串口处理线程
        /// </summary>
        private static void StartCommThread()
        {
            CommLock = new object();
            CommMutex = new Mutex();
            SendThread = new Thread(SendMessageThread);
            //ReceiveThread = new Thread(ReceiveMessageThread);
            SendThread.Start();
            //ReceiveThread.Start();
        }

        /// <summary>
        /// 停止串口处理线程
        /// </summary>
        private static void StopCommThread()
        {
            if (SendThread != null) SendThread.Abort();
            if (ReceiveThread != null) ReceiveThread.Abort();
        }

        /// <summary>
        /// 发送线程方法
        /// </summary>
        /// <param name="state"></param>
        private static void SendMessageThread(object state)
        {            
            while (true)
            {
                try
                {
                    Monitor.Enter(CommLock);
                    CommMutex.WaitOne();
                    var startTime = DateTime.Now;
                    try
                    {
                        AutoSendMessage();
                    }
                    catch { }
                    var timeSpan = SendWaitTime - (DateTime.Now - startTime).TotalMilliseconds;
                    if (timeSpan > 0) Thread.Sleep((int)timeSpan);                    
                }
                catch (Exception ex)
                {
                }
                finally
                {
                   CommMutex.ReleaseMutex();
                   Monitor.Exit(CommLock);
                }
            }
        }

        /// <summary>
        /// 接收线程方法
        /// </summary>
        /// <param name="state"></param>
        private static void ReceiveMessageThread(object state)
        {
            while (true)
            {
                try
                {
                    Monitor.Enter(CommLock);
                    CommMutex.WaitOne();
                    var startTime = DateTime.Now;
                    try
                    {
                        AutoReceiveMessage();
                    }
                    catch { }
                    var timeSpan = ReceiveWaitTime - (DateTime.Now - startTime).TotalMilliseconds;
                    if (timeSpan > 0) Thread.Sleep((int)timeSpan);
                }
                finally
                {
                    CommMutex.ReleaseMutex();
                    Monitor.Exit(CommLock);
                }
            }
        }

        /// <summary>
        /// 自动发送短信
        /// </summary>
        private static void AutoSendMessage()
        {
            //不自动发送
            if (!AutoSendState) return;
            using (var dc = new SMSAccess())
            {
                if (SendList == null || SendList.Count == 0)
                {
                    SendList = dc.GetWaittingMessage(10);
                }
                if (SendList != null && SendList.Count > 0)
                {
                    var retries = 0;
                    var msg = string.Empty;
                    var exType = ExceptionType.Unknow;
                    var sendOk = false;
                    var nCount = SendList.Count;
                    for (var i = 0; i < nCount; i++)
                    {
                        var sendMsg = SendList[i];
                        if (!AutoSendState || ImmediateState) break;
                        sendOk = SendSplitMessage(sendMsg.ReceiveNumber, sendMsg.SendContent, out retries, out msg, out exType);
                        if (!sendOk && exType == ExceptionType.Inner) continue;
                        sendMsg.Retries = retries;
                        sendMsg.Status = sendOk ? (int)Status.Success : (int)Status.Fail;
                        sendMsg.HandleTime = DateTime.Now;
                        sendMsg.HandleResult = msg;
                        dc.UpdateEntity<SendMessage>(sendMsg);
                        SendList.RemoveAt(i);
                        i--;
                    }
                }
            }
        }

        /// <summary>
        /// 发送拆分的短信
        /// </summary>
        /// <param name="number"></param>
        /// <param name="content"></param>
        /// <param name="retries"></param>
        /// <param name="msg"></param>
        /// <param name="exType"></param>
        /// <returns></returns>
        private static bool SendSplitMessage(string number, string content, out int retries, out string msg, out ExceptionType exType)
        {
            retries = 0;
            msg = string.Empty;
            exType = ExceptionType.Unknow;
            var arrContent = SplitText(content, 70);
            var sendOk = false;
            foreach (var sContent in arrContent)
            {
                sendOk = SendSMS(number, sContent, out retries, out msg, out exType);
                if (!sendOk) break;
            }
            return sendOk;
        }

        /// <summary>
        /// 拆分字符串
        /// </summary>
        /// <param name="text"></param>
        /// <param name="splitLen"></param>
        /// <returns></returns>
        private static List<string> SplitText(string text, int splitLen)
        {
            var textList = new List<string>();
            var textLen = text.Length;
            if (string.IsNullOrEmpty(text)) return textList;
            if (textLen <= splitLen)
            {
                textList.Add(text);
                return textList;
            }
            var splitCount = textLen / splitLen;
            if ((textLen % splitLen) > 0) splitCount += 1;
            for (var i = 0; i < splitCount; i++)
            {
                if (i == splitCount - 1)
                {
                    textList.Add(text.Substring(i * splitLen));
                }
                else
                {
                    textList.Add(text.Substring(i * splitLen, splitLen));
                }
            }
            return textList;
        }

        /// <summary>
        /// 自动接收短信
        /// </summary>
        private static void AutoReceiveMessage()
        {
            //不自动接收
            if (!AutoReceiveState) return;
        }

        #endregion
    }
}